package com.wechat.page.contact.card;

import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;

import com.swak.entity.Toggle;
import com.swak.fx.support.AbstractComp;
import com.swak.fx.support.AbstractPage;
import com.swak.fx.support.FXMLView;
import com.swak.fx.support.ui.UI;
import com.swak.fx.support.ui.UIEvent;
import com.swak.utils.Lists;
import com.swak.utils.Maps;
import com.swak.utils.StringUtils;
import com.wechat.Cons;
import com.wechat.dto.ChatDTO;
import com.wechat.dto.ChatRoomDTO;
import com.wechat.dto.ContactDTO;
import com.wechat.enums.ContactType;
import com.wechat.page.api.ChatRoomApi;
import com.wechat.page.api.ContactApi;
import com.wechat.page.comps.TextComp;
import com.wechat.page.contact.misc.ContactThumb;
import com.wechat.page.contact.misc.OpsThumb;
import com.wechat.page.contact.select.ContactSelect;

import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ListProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ListChangeListener.Change;
import javafx.scene.Node;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.GridPane;

/**
 * ContactThumbComp. 只显示头像和名称
 * 
 * @author DELL
 */
@FXMLView(title = "联系人 - 列表", value = "ContactThumbsComp.fxml", css = "ContactThumbsComp.css")
public class ContactThumbsComp extends AbstractComp implements TextComp {

	private FlowPane content;

	@Autowired
	private ContactApi contactApi;
	@Autowired
	private ChatRoomApi chatRoomApi;

	// 添加按钮和删除按钮
	private boolean hasOps = false;
	private OpsThumb addBtn;
	private OpsThumb subBtn;

	public ContactThumbsComp(AbstractPage parent) {
		super(parent);
		this.content = (FlowPane) super.root;
		this.addBtn = new OpsThumb(UI.Icons.PLUS, "添加");
		this.subBtn = new OpsThumb(UI.Icons.MINUS, "减少");
		this.addBtn.getStyleClass().add("list-page__ph");
		this.subBtn.getStyleClass().add("list-page__ph");
	}

	@Override
	public void initializeParts() {
		super.initializeParts();
	}

	@Override
	public void setupEventHandlers() {
		super.setupEventHandlers();
		this.addBtn.addEventHandler(MouseEvent.MOUSE_CLICKED, (e) -> {
			ContactSelect contactSelect = ContactSelect.New();
			contactSelect.setChat(this.chatProperty.get());
			contactSelect.show();
		});
		this.root.addEventHandler(UIEvent.UI, (e) -> this.onContactHandler(e));
	}

	@SuppressWarnings("unchecked")
	private void onContactHandler(UIEvent event) {
		if (event.getCmd().equals(ContactThumb.SelectEvent)) {
			Toggle<ContactDTO> select = (Toggle<ContactDTO>) event.getData();
			if (select.isActive()) {
				this.selectiveProperty.add(select.getValue());
			} else {
				this.selectiveProperty.remove(select.getValue());
			}
		}
	}

	@Override
	public void setupBindings() {
		super.setupBindings();
		this.subBtn.visibleProperty().bind(this.isChatroomProperty);
		this.subBtn.managedProperty().bind(this.isChatroomProperty);
	}

	@Override
	public void setupValueChangedListeners() {
		super.setupValueChangedListeners();
		this.chatProperty.addListener((ob, o, n) -> this.$runUI(() -> this.onChatChange(n)));
		this.contactProperty.addListener((ob, o, n) -> this.onLoadContact(n));
		this.chatroomProperty.addListener((ob, o, n) -> this.onLoadContacts(n));
		this.contactsProperty.addListener(this.contactsChangeListener);
	}

	private void onChatChange(ChatDTO chat) {
		this.content.setVisible(false);
		this.contactsProperty.removeListener(contactsChangeListener);
		this.content.getChildren().clear();
		this.hasOps = false;
		this.Add2Box(this.addBtn);
		this.Add2Box(this.subBtn);
		this.hasOps = true;
		this.contactPanes.values().forEach((node) -> {
			node.destory();
		});
		this.contactPanes.clear();
		this.contactsProperty.addListener(contactsChangeListener);
		this.content.setVisible(true);
		this.onLoadContact(chat);
	}

	private final ListChangeListener<? super ContactDTO> contactsChangeListener = this::onContactsChange;

	@SuppressWarnings("unchecked")
	private void onContactsChange(Change<? extends ContactDTO> c) {
		this.$runUI(() -> {
			while (c.next()) {
				if (c.wasAdded()) {
					List<ContactDTO> adds = (List<ContactDTO>) c.getAddedSubList();
					for (ContactDTO contact : adds) {
						this.onAddContact(contact);
					}
				} else if (c.wasRemoved()) {
					List<ContactDTO> adds = (List<ContactDTO>) c.getRemoved();
					for (ContactDTO contact : adds) {
						this.onRemoveContact(contact);
					}
				}
			}
		});
	}

	private void onAddContact(ContactDTO contact) {

		// 只能添加一次
		if (this.hasContact(contact)) {
			return;
		}

		// 实例化界面
		ContactThumb cardThumb = new ContactThumb();
		cardThumb.removeAbleProperty().bind(this.removeAbleProperty);
		cardThumb.selectAbleProperty().bind(this.selectAbleProperty);
		cardThumb.getStyleClass().add("list-page__ph");
		cardThumb.getProperties().put(UI.Conts.ID_Key, contact.getId());
		cardThumb.getProperties().put(UI.Conts.Data_Key, contact);

		// 保存列表实例
		this.saveContactNode(contact, cardThumb);

		// 设置 Chat
		this.Add2BoxBeforeOps(cardThumb);
		cardThumb.contactProperty().set(contact);
	}

	private void Add2Box(Node node) {
		this.content.getChildren().add(node);
		GridPane.setFillHeight(node, true);
		GridPane.setFillWidth(node, true);
	}

	private void Add2BoxBeforeOps(Node node) {
		if (this.hasOps) {
			int last = this.content.getChildren().size();
			this.content.getChildren().add(last - 2, node);
		} else {
			this.content.getChildren().add(node);
		}
		GridPane.setFillHeight(node, true);
		GridPane.setFillWidth(node, true);
	}

	private void onRemoveContact(ContactDTO contact) {
		ContactThumb node = this.findContactNode(contact);
		if (node != null) {
			this.removeChild(node);
			this.removeContactNode(contact);
		}
	}

	@Override
	public void initializeFinish() {
		super.initializeFinish();
	}

	// ================================================================================
	// Chat <==> Pane
	// ================================================================================
	private Map<String, ContactThumb> contactPanes = Maps.newHashMap();

	private synchronized boolean hasContact(ContactDTO chat) {
		return contactPanes.containsKey(chat.getId());
	}

	private synchronized void saveContactNode(ContactDTO chat, ContactThumb inner) {
		this.contactPanes.put(chat.getId(), inner);
	}

	private synchronized ContactThumb findContactNode(ContactDTO chat) {
		return this.contactPanes.get(chat.getId());
	}

	private synchronized void removeContactNode(ContactDTO chat) {
		this.contactPanes.remove(chat.getId());
	}

	// ================================================================================
	// 加载数据
	// ================================================================================
	private void onLoadContact(ChatDTO chat) {
		if (chat != null) {
			this.$run(() -> contactApi.get(chat.getId()).thenAccept(contact -> this.contactProperty.set(contact)));
		}
	}

	private void onLoadContact(ContactDTO contact) {
		if (contact == null) {
			return;
		}

		// 查询具体的联系人
		if (contact.getType() != null && contact.getType() == ContactType.Chat_Room.getType()) {
			this.onLoadChatRoom(contact);
		}

		// 直接添加
		else {
			this.contactsProperty.add(contact);
		}
	}

	private void onLoadChatRoom(ContactDTO contact) {
		this.$run(() -> chatRoomApi.get(contact.getId()).thenAccept(room -> this.chatroomProperty.set(room)));
	}

	private void onLoadContacts(ChatRoomDTO room) {
		if (room == null) {
			return;
		}
		String[] userNames = StringUtils.split(room.getUserNames(), Cons.UserNamesSeparateChar);
		this.$run(() -> this.contactApi.contacts(Lists.newArrayList(userNames))
				.thenAccept(cs -> this.contactsProperty.addAll(cs)));
	}

	private final ListProperty<ContactDTO> selectiveProperty = new SimpleListProperty<>(
			FXCollections.observableArrayList());
	private final ListProperty<ContactDTO> contactsProperty = new SimpleListProperty<>(
			FXCollections.observableArrayList());
	private final ObjectProperty<ChatRoomDTO> chatroomProperty = new SimpleObjectProperty<>();
	private final ObjectProperty<ContactDTO> contactProperty = new SimpleObjectProperty<>();
	private final ObjectProperty<ChatDTO> chatProperty = new SimpleObjectProperty<>();

	private BooleanBinding isChatroomProperty = Bindings.createBooleanBinding(() -> {
		ChatDTO chat = this.chatProperty.get();
		return chat != null && chat.getType() == ContactType.Chat_Room.getType();
	}, this.chatProperty);

	private BooleanProperty removeAbleProperty = new SimpleBooleanProperty(false);

	public BooleanProperty removeAbleProperty() {
		return removeAbleProperty;
	}

	private BooleanProperty selectAbleProperty = new SimpleBooleanProperty(false);

	public BooleanProperty selectAbleProperty() {
		return selectAbleProperty;
	}

	public List<ContactDTO> getSelective() {
		return Lists.newArrayList(selectiveProperty.get());
	}

	// ================================================================================
	// 方式1：通过此属性来监听外部事件的变化 方式1
	// ================================================================================
	public ObjectProperty<ChatDTO> chatProperty() {
		return chatProperty;
	}

	// ================================================================================
	// 方式2：直接设置数据
	// ================================================================================
	public ListProperty<ContactDTO> contactsProperty() {
		return contactsProperty;
	}
}